/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"vm/ArrayBufferObject-inl.h"#include"vm/ArrayBufferObject.h"#include"mozilla/Alignment.h"#include"mozilla/CheckedInt.h"#include"mozilla/FloatingPoint.h"#include"mozilla/Maybe.h"#include"mozilla/PodOperations.h"#include"mozilla/TaggedAnonymousMemory.h"#include<string.h>#ifndef XP_WIN# include <sys/mman.h>#endif#ifdef MOZ_VALGRIND# include <valgrind/memcheck.h>#endif#include"jsapi.h"#include"jsarray.h"#include"jscntxt.h"#include"jscpucfg.h"#include"jsfriendapi.h"#include"jsnum.h"#include"jsobj.h"#include"jstypes.h"#include"jsutil.h"#ifdef XP_WIN# include "jswin.h"#endif#include"jswrapper.h"#include"builtin/DataViewObject.h"#include"gc/Barrier.h"#include"gc/Marking.h"#include"gc/Memory.h"#include"js/Conversions.h"#include"js/MemoryMetrics.h"#include"vm/GlobalObject.h"#include"vm/Interpreter.h"#include"vm/SharedArrayObject.h"#include"vm/WrapperObject.h"#include"wasm/WasmSignalHandlers.h"#include"wasm/WasmTypes.h"#include"jsatominlines.h"#include"vm/NativeObject-inl.h"#include"vm/Shape-inl.h"usingJS::ToInt32;usingmozilla::DebugOnly;usingmozilla::CheckedInt;usingmozilla::Some;usingmozilla::Maybe;usingmozilla::Nothing;usingnamespacejs;usingnamespacejs::gc;/* * Convert |v| to an array index for an array of length |length| per * the Typed Array Specification section 7.0, |subarray|. If successful, * the output value is in the range [0, length]. */booljs::ToClampedIndex(JSContext*cx,HandleValuev,uint32_tlength,uint32_t*out){int32_tresult;if(!ToInt32(cx,v,&result))returnfalse;if(result<0){result+=length;if(result<0)result=0;}elseif(uint32_t(result)>length){result=length;}*out=uint32_t(result);returntrue;}/* * ArrayBufferObject * * This class holds the underlying raw buffer that the TypedArrayObject classes * access. It can be created explicitly and passed to a TypedArrayObject, or * can be created implicitly by constructing a TypedArrayObject with a size. *//* * ArrayBufferObject (base) */staticJSObject*CreateArrayBufferPrototype(JSContext*cx,JSProtoKeykey){returnGlobalObject::createBlankPrototype(cx,cx->global(),&ArrayBufferObject::protoClass_);}staticconstClassOpsArrayBufferObjectClassOps={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */ArrayBufferObject::finalize,nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */ArrayBufferObject::trace,};staticconstJSFunctionSpecstatic_functions[]={JS_FN("isView",ArrayBufferObject::fun_isView,1,0),JS_FS_END};staticconstJSPropertySpecstatic_properties[]={JS_SELF_HOSTED_SYM_GET(species,"ArrayBufferSpecies",0),JS_PS_END};staticconstJSFunctionSpecprototype_functions[]={JS_SELF_HOSTED_FN("slice","ArrayBufferSlice",2,0),JS_FS_END};staticconstJSPropertySpecprototype_properties[]={JS_PSG("byteLength",ArrayBufferObject::byteLengthGetter,0),JS_STRING_SYM_PS(toStringTag,"ArrayBuffer",JSPROP_READONLY),JS_PS_END};staticconstClassSpecArrayBufferObjectClassSpec={GenericCreateConstructor<ArrayBufferObject::class_constructor,1,gc::AllocKind::FUNCTION>,CreateArrayBufferPrototype,static_functions,static_properties,prototype_functions,prototype_properties};staticconstClassExtensionArrayBufferObjectClassExtension={nullptr,/* weakmapKeyDelegateOp */ArrayBufferObject::objectMoved};constClassArrayBufferObject::class_={"ArrayBuffer",JSCLASS_DELAY_METADATA_BUILDER|JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS)|JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer)|JSCLASS_BACKGROUND_FINALIZE,&ArrayBufferObjectClassOps,&ArrayBufferObjectClassSpec,&ArrayBufferObjectClassExtension};constClassArrayBufferObject::protoClass_={"ArrayBufferPrototype",JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),JS_NULL_CLASS_OPS,&ArrayBufferObjectClassSpec};booljs::IsArrayBuffer(HandleValuev){returnv.isObject()&&v.toObject().is<ArrayBufferObject>();}booljs::IsArrayBuffer(HandleObjectobj){returnobj->is<ArrayBufferObject>();}booljs::IsArrayBuffer(JSObject*obj){returnobj->is<ArrayBufferObject>();}ArrayBufferObject&js::AsArrayBuffer(HandleObjectobj){MOZ_ASSERT(IsArrayBuffer(obj));returnobj->as<ArrayBufferObject>();}ArrayBufferObject&js::AsArrayBuffer(JSObject*obj){MOZ_ASSERT(IsArrayBuffer(obj));returnobj->as<ArrayBufferObject>();}booljs::IsArrayBufferMaybeShared(HandleValuev){returnv.isObject()&&v.toObject().is<ArrayBufferObjectMaybeShared>();}booljs::IsArrayBufferMaybeShared(HandleObjectobj){returnobj->is<ArrayBufferObjectMaybeShared>();}booljs::IsArrayBufferMaybeShared(JSObject*obj){returnobj->is<ArrayBufferObjectMaybeShared>();}ArrayBufferObjectMaybeShared&js::AsArrayBufferMaybeShared(HandleObjectobj){MOZ_ASSERT(IsArrayBufferMaybeShared(obj));returnobj->as<ArrayBufferObjectMaybeShared>();}ArrayBufferObjectMaybeShared&js::AsArrayBufferMaybeShared(JSObject*obj){MOZ_ASSERT(IsArrayBufferMaybeShared(obj));returnobj->as<ArrayBufferObjectMaybeShared>();}MOZ_ALWAYS_INLINEboolArrayBufferObject::byteLengthGetterImpl(JSContext*cx,constCallArgs&args){MOZ_ASSERT(IsArrayBuffer(args.thisv()));args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());returntrue;}boolArrayBufferObject::byteLengthGetter(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnCallNonGenericMethod<IsArrayBuffer,byteLengthGetterImpl>(cx,args);}/* * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1 */boolArrayBufferObject::fun_isView(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);args.rval().setBoolean(args.get(0).isObject()&&JS_IsArrayBufferViewObject(&args.get(0).toObject()));returntrue;}// ES2017 draft 24.1.2.1boolArrayBufferObject::class_constructor(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Step 1.if(!ThrowIfNotConstructing(cx,args,"ArrayBuffer"))returnfalse;// Step 2.uint64_tbyteLength;if(!ToIndex(cx,args.get(0),&byteLength))returnfalse;// Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).// 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).RootedObjectproto(cx);RootedObjectnewTarget(cx,&args.newTarget().toObject());if(!GetPrototypeFromConstructor(cx,newTarget,&proto))returnfalse;// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).// Refuse to allocate too large buffers, currently limited to ~2 GiB.if(byteLength>INT32_MAX){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnfalse;}// 24.1.1.1, steps 1 and 4-6.JSObject*bufobj=create(cx,uint32_t(byteLength),proto);if(!bufobj)returnfalse;args.rval().setObject(*bufobj);returntrue;}staticArrayBufferObject::BufferContentsAllocateArrayBufferContents(JSContext*cx,uint32_tnbytes){uint8_t*p=cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);if(!p)ReportOutOfMemory(cx);returnArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);}staticvoidNoteViewBufferWasDetached(ArrayBufferViewObject*view,ArrayBufferObject::BufferContentsnewContents,JSContext*cx){view->notifyBufferDetached(cx,newContents.data());// Notify compiled jit code that the base pointer has moved.MarkObjectStateChange(cx,view);}/* static */voidArrayBufferObject::detach(JSContext*cx,Handle<ArrayBufferObject*>buffer,BufferContentsnewContents){assertSameCompartment(cx,buffer);MOZ_ASSERT(!buffer->isPreparedForAsmJS());// When detaching buffers where we don't know all views, the new data must// match the old data. All missing views are typed objects, which do not// expect their data to ever change.MOZ_ASSERT_IF(buffer->forInlineTypedObject(),newContents.data()==buffer->dataPointer());// When detaching a buffer with typed object views, any jitcode accessing// such views must be deoptimized so that detachment checks are performed.// This is done by setting a compartment-wide flag indicating that buffers// with typed object views have been detached.if(buffer->hasTypedObjectViews()){// Make sure the global object's group has been instantiated, so the// flag change will be observed.AutoEnterOOMUnsafeRegionoomUnsafe;if(!JSObject::getGroup(cx,cx->global()))oomUnsafe.crash("ArrayBufferObject::detach");MarkObjectGroupFlags(cx,cx->global(),OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);cx->compartment()->detachedTypedObjects=1;}// Update all views of the buffer to account for the buffer having been// detached, and clear the buffer's data and list of views.auto&innerViews=cx->compartment()->innerViews.get();if(InnerViewTable::ViewVector*views=innerViews.maybeViewsUnbarriered(buffer)){for(size_ti=0;i<views->length();i++)NoteViewBufferWasDetached((*views)[i],newContents,cx);innerViews.removeViews(buffer);}if(buffer->firstView()){if(buffer->forInlineTypedObject()){// The buffer points to inline data in its first view, so to keep// this pointer alive we don't clear out the first view.MOZ_ASSERT(buffer->firstView()->is<InlineTransparentTypedObject>());}else{NoteViewBufferWasDetached(buffer->firstView(),newContents,cx);buffer->setFirstView(nullptr);}}if(newContents.data()!=buffer->dataPointer())buffer->setNewData(cx->runtime()->defaultFreeOp(),newContents,OwnsData);buffer->setByteLength(0);buffer->setIsDetached();}voidArrayBufferObject::setNewData(FreeOp*fop,BufferContentsnewContents,OwnsStateownsState){if(ownsData()){MOZ_ASSERT(newContents.data()!=dataPointer());releaseData(fop);}setDataPointer(newContents,ownsState);}// This is called *only* from changeContents(), below.// By construction, every view parameter will be mapping unshared memory (an ArrayBuffer).// Hence no reason to worry about shared memory here.voidArrayBufferObject::changeViewContents(JSContext*cx,ArrayBufferViewObject*view,uint8_t*oldDataPointer,BufferContentsnewContents){MOZ_ASSERT(!view->isSharedMemory());// Watch out for NULL data pointers in views. This means that the view// is not fully initialized (in which case it'll be initialized later// with the correct pointer).JS::AutoCheckCannotGCnogc;uint8_t*viewDataPointer=view->dataPointerUnshared(nogc);if(viewDataPointer){MOZ_ASSERT(newContents);ptrdiff_toffset=viewDataPointer-oldDataPointer;viewDataPointer=static_cast<uint8_t*>(newContents.data())+offset;view->setDataPointerUnshared(viewDataPointer);}// Notify compiled jit code that the base pointer has moved.MarkObjectStateChange(cx,view);}// BufferContents is specific to ArrayBuffer, hence it will not represent shared memory.voidArrayBufferObject::changeContents(JSContext*cx,BufferContentsnewContents,OwnsStateownsState){MOZ_RELEASE_ASSERT(!isWasm());MOZ_ASSERT(!forInlineTypedObject());// Change buffer contents.uint8_t*oldDataPointer=dataPointer();setNewData(cx->runtime()->defaultFreeOp(),newContents,ownsState);// Update all views.auto&innerViews=cx->compartment()->innerViews.get();if(InnerViewTable::ViewVector*views=innerViews.maybeViewsUnbarriered(this)){for(size_ti=0;i<views->length();i++)changeViewContents(cx,(*views)[i],oldDataPointer,newContents);}if(firstView())changeViewContents(cx,firstView(),oldDataPointer,newContents);}/* * Wasm Raw Buf Linear Memory Structure * * The linear heap in Wasm is an mmaped array buffer. Several * constants manage its lifetime: * * - length - the wasm-visible current length of the buffer. Accesses in the * range [0, length] succeed. May only increase. * * - boundsCheckLimit - when !WASM_HUGE_MEMORY, the size against which we * perform bounds checks. It is always a constant offset smaller than * mappedSize. Currently that constant offset is 64k (wasm::GuardSize). * * - max - the optional declared limit on how much length can grow. * * - mappedSize - the actual mmaped size. Access in the range * [0, mappedSize] will either succeed, or be handled by the wasm signal * handlers. * * The below diagram shows the layout of the wasm heap. The wasm-visible * portion of the heap starts at 0. There is one extra page prior to the * start of the wasm heap which contains the WasmArrayRawBuffer struct at * its end (i.e. right before the start of the WASM heap). * * WasmArrayRawBuffer * \ ArrayBufferObject::dataPointer() * \ / * \ | * ______|_|____________________________________________________________ * |______|_|______________|___________________|____________|____________| * 0 length maxSize boundsCheckLimit mappedSize * * \_______________________/ * COMMITED * \____________________________________________/ * SLOP * \_____________________________________________________________________/ * MAPPED * * Invariants: * - length only increases * - 0 <= length <= maxSize (if present) <= boundsCheckLimit <= mappedSize * - on ARM boundsCheckLimit must be a valid ARM immediate. * - if maxSize is not specified, boundsCheckLimit/mappedSize may grow. They are * otherwise constant. * * NOTE: For asm.js on non-x64 we guarantee that * * length == maxSize == boundsCheckLimit == mappedSize * * That is, signal handlers will not be invoked, since they cannot emulate * asm.js accesses on non-x64 architectures. * * The region between length and mappedSize is the SLOP - an area where we use * signal handlers to catch things that slip by bounds checks. Logically it has * two parts: * * - from length to boundsCheckLimit - this part of the SLOP serves to catch * accesses to memory we have reserved but not yet grown into. This allows us * to grow memory up to max (when present) without having to patch/update the * bounds checks. * * - from boundsCheckLimit to mappedSize - (Note: In current patch 0) - this * part of the SLOP allows us to bounds check against base pointers and fold * some constant offsets inside loads. This enables better Bounds * Check Elimination. * */classjs::WasmArrayRawBuffer{Maybe<uint32_t>maxSize_;size_tmappedSize_;protected:WasmArrayRawBuffer(uint8_t*buffer,uint32_tlength,constMaybe<uint32_t>&maxSize,size_tmappedSize):maxSize_(maxSize),mappedSize_(mappedSize){MOZ_ASSERT(buffer==dataPointer());}public:staticWasmArrayRawBuffer*Allocate(uint32_tnumBytes,constMaybe<uint32_t>&maxSize);staticvoidRelease(void*mem);uint8_t*dataPointer(){uint8_t*ptr=reinterpret_cast<uint8_t*>(this);returnptr+sizeof(WasmArrayRawBuffer);}uint8_t*basePointer(){returndataPointer()-gc::SystemPageSize();}size_tmappedSize()const{returnmappedSize_;}Maybe<uint32_t>maxSize()const{returnmaxSize_;}size_tallocatedBytes()const{returnmappedSize_+gc::SystemPageSize();}#ifndef WASM_HUGE_MEMORYuint32_tboundsCheckLimit()const{MOZ_ASSERT(mappedSize_<=UINT32_MAX);MOZ_ASSERT(mappedSize_>=wasm::GuardSize);MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize_-wasm::GuardSize));returnmappedSize_-wasm::GuardSize;}#endifMOZ_MUST_USEboolgrowToSizeInPlace(uint32_toldSize,uint32_tnewSize){MOZ_ASSERT(newSize>=oldSize);MOZ_ASSERT_IF(maxSize(),newSize<=maxSize().value());MOZ_ASSERT(newSize<=mappedSize());uint32_tdelta=newSize-oldSize;MOZ_ASSERT(delta%wasm::PageSize==0);uint8_t*dataEnd=dataPointer()+oldSize;MOZ_ASSERT(uintptr_t(dataEnd)%gc::SystemPageSize()==0);# ifdef XP_WINif(delta&&!VirtualAlloc(dataEnd,delta,MEM_COMMIT,PAGE_READWRITE))returnfalse;# else // XP_WINif(delta&&mprotect(dataEnd,delta,PROT_READ|PROT_WRITE))returnfalse;# endif // !XP_WIN# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsignedchar*)dataEnd,delta);# endifMemProfiler::SampleNative(dataEnd,delta);returntrue;}#ifndef WASM_HUGE_MEMORYboolextendMappedSize(uint32_tmaxSize){size_tnewMappedSize=wasm::ComputeMappedSize(maxSize);MOZ_ASSERT(mappedSize_<=newMappedSize);if(mappedSize_==newMappedSize)returntrue;# ifdef XP_WINuint8_t*mappedEnd=dataPointer()+mappedSize_;uint32_tdelta=newMappedSize-mappedSize_;if(!VirtualAlloc(mappedEnd,delta,MEM_RESERVE,PAGE_NOACCESS))returnfalse;# elif defined(XP_LINUX)// Note this will not move memory (no MREMAP_MAYMOVE specified)if(MAP_FAILED==mremap(dataPointer(),mappedSize_,newMappedSize,0))returnfalse;# else// No mechanism for remapping on MacOS and other Unices. Luckily// shouldn't need it here as most of these are 64-bit.returnfalse;# endifmappedSize_=newMappedSize;returntrue;}// Try and grow the mapped region of memory. Does not changes current size.// Does not move memory if no space to grow.voidtryGrowMaxSizeInPlace(uint32_tdeltaMaxSize){CheckedInt<uint32_t>newMaxSize=maxSize_.value();newMaxSize+=deltaMaxSize;MOZ_ASSERT(newMaxSize.isValid());MOZ_ASSERT(newMaxSize.value()%wasm::PageSize==0);if(!extendMappedSize(newMaxSize.value()))return;maxSize_=Some(newMaxSize.value());}#endif // WASM_HUGE_MEMORY};/* static */WasmArrayRawBuffer*WasmArrayRawBuffer::Allocate(uint32_tnumBytes,constMaybe<uint32_t>&maxSize){size_tmappedSize;#ifdef WASM_HUGE_MEMORYmappedSize=wasm::HugeMappedSize;#elsemappedSize=wasm::ComputeMappedSize(maxSize.valueOr(numBytes));#endifMOZ_RELEASE_ASSERT(mappedSize<=SIZE_MAX-gc::SystemPageSize());MOZ_RELEASE_ASSERT(numBytes<=maxSize.valueOr(UINT32_MAX));MOZ_ASSERT(numBytes%gc::SystemPageSize()==0);MOZ_ASSERT(mappedSize%gc::SystemPageSize()==0);uint64_tmappedSizeWithHeader=mappedSize+gc::SystemPageSize();uint64_tnumBytesWithHeader=numBytes+gc::SystemPageSize();# ifdef XP_WINvoid*data=VirtualAlloc(nullptr,(size_t)mappedSizeWithHeader,MEM_RESERVE,PAGE_NOACCESS);if(!data)returnnullptr;if(!VirtualAlloc(data,numBytesWithHeader,MEM_COMMIT,PAGE_READWRITE)){VirtualFree(data,0,MEM_RELEASE);returnnullptr;}# else // XP_WINvoid*data=MozTaggedAnonymousMmap(nullptr,(size_t)mappedSizeWithHeader,PROT_NONE,MAP_PRIVATE|MAP_ANON,-1,0,"wasm-reserved");if(data==MAP_FAILED)returnnullptr;// Note we will waste a page on zero-sized memories hereif(mprotect(data,numBytesWithHeader,PROT_READ|PROT_WRITE)){munmap(data,mappedSizeWithHeader);returnnullptr;}# endif // !XP_WINMemProfiler::SampleNative(data,numBytesWithHeader);# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsignedchar*)data+numBytesWithHeader,mappedSizeWithHeader-numBytesWithHeader);# endifuint8_t*base=reinterpret_cast<uint8_t*>(data)+gc::SystemPageSize();uint8_t*header=base-sizeof(WasmArrayRawBuffer);autorawBuf=new(header)WasmArrayRawBuffer(base,numBytes,maxSize,mappedSize);returnrawBuf;}/* static */voidWasmArrayRawBuffer::Release(void*mem){WasmArrayRawBuffer*header=(WasmArrayRawBuffer*)((uint8_t*)mem-sizeof(WasmArrayRawBuffer));uint8_t*base=header->basePointer();MOZ_RELEASE_ASSERT(header->mappedSize()<=SIZE_MAX-gc::SystemPageSize());size_tmappedSizeWithHeader=header->mappedSize()+gc::SystemPageSize();MemProfiler::RemoveNative(base);# ifdef XP_WINVirtualFree(base,0,MEM_RELEASE);# else // XP_WINmunmap(base,mappedSizeWithHeader);# endif // !XP_WIN# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base,mappedSizeWithHeader);# endif}WasmArrayRawBuffer*ArrayBufferObject::BufferContents::wasmBuffer()const{MOZ_RELEASE_ASSERT(kind_==WASM);return(WasmArrayRawBuffer*)(data_-sizeof(WasmArrayRawBuffer));}#define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))/* static */ArrayBufferObject*ArrayBufferObject::createForWasm(JSContext*cx,uint32_tinitialSize,constMaybe<uint32_t>&maybeMaxSize){MOZ_ASSERT(initialSize%wasm::PageSize==0);MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());MOZ_RELEASE_ASSERT((initialSize/wasm::PageSize)<=wasm::MaxMemoryInitialPages);// Prevent applications specifying a large max (like UINT32_MAX) from// unintentially OOMing the browser on 32-bit: they just want "a lot of// memory". Maintain the invariant that initialSize <= maxSize.Maybe<uint32_t>maxSize=maybeMaxSize;if(sizeof(void*)==4&&maybeMaxSize){staticconstuint32_tOneGiB=1<<30;uint32_tclamp=Max(OneGiB,initialSize);maxSize=Some(Min(clamp,maybeMaxSize.value()));}RootedArrayBufferObjectbuffer(cx,ArrayBufferObject::createEmpty(cx));if(!buffer)returnnullptr;// Try to reserve the maximum requested memoryWasmArrayRawBuffer*wasmBuf=WasmArrayRawBuffer::Allocate(initialSize,maxSize);if(!wasmBuf){#ifdef WASM_HUGE_MEMORYReportOutOfMemory(cx);returnnullptr;#else// If we fail, and have a maxSize, try to reserve the biggest chunk in// the range [initialSize, maxSize) using log backoff.if(!maxSize){ReportOutOfMemory(cx);returnnullptr;}uint32_tcur=maxSize.value()/2;for(;cur>initialSize;cur/=2){wasmBuf=WasmArrayRawBuffer::Allocate(initialSize,Some(ROUND_UP(cur,wasm::PageSize)));if(wasmBuf)break;}if(!wasmBuf){ReportOutOfMemory(cx);returnnullptr;}// Try to grow our chunk as much as possible.for(size_td=cur/2;d>=wasm::PageSize;d/=2)wasmBuf->tryGrowMaxSizeInPlace(ROUND_UP(d,wasm::PageSize));#endif}autocontents=BufferContents::create<WASM>(wasmBuf->dataPointer());buffer->initialize(initialSize,contents,OwnsData);cx->zone()->updateMallocCounter(wasmBuf->mappedSize());returnbuffer;}// Note this function can return false with or without an exception pending. The// asm.js caller checks cx->isExceptionPending before propagating failure.// Returning false without throwing means that asm.js linking will fail which// will recompile as non-asm.js./* static */boolArrayBufferObject::prepareForAsmJS(JSContext*cx,Handle<ArrayBufferObject*>buffer,boolneedGuard){#ifdef WASM_HUGE_MEMORYMOZ_ASSERT(needGuard);#endifMOZ_ASSERT(buffer->byteLength()%wasm::PageSize==0);MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());if(buffer->forInlineTypedObject())returnfalse;if(needGuard){if(buffer->isWasm()&&buffer->isPreparedForAsmJS())returntrue;// Non-prepared-for-asm.js wasm buffers can be detached at any time.// This error can only be triggered for SIMD.js (which isn't shipping)// on !WASM_HUGE_MEMORY so this error is only visible in testing.if(buffer->isWasm()||buffer->isPreparedForAsmJS())returnfalse;uint32_tlength=buffer->byteLength();WasmArrayRawBuffer*wasmBuf=WasmArrayRawBuffer::Allocate(length,Some(length));if(!wasmBuf){ReportOutOfMemory(cx);returnfalse;}void*data=wasmBuf->dataPointer();memcpy(data,buffer->dataPointer(),length);// Swap the new elements into the ArrayBufferObject. Mark the// ArrayBufferObject so we don't do this again.buffer->changeContents(cx,BufferContents::create<WASM>(data),OwnsData);buffer->setIsPreparedForAsmJS();MOZ_ASSERT(data==buffer->dataPointer());cx->zone()->updateMallocCounter(wasmBuf->mappedSize());returntrue;}if(!buffer->isWasm()&&buffer->isPreparedForAsmJS())returntrue;// Non-prepared-for-asm.js wasm buffers can be detached at any time.if(buffer->isWasm())returnfalse;if(!buffer->ownsData()){BufferContentscontents=AllocateArrayBufferContents(cx,buffer->byteLength());if(!contents)returnfalse;memcpy(contents.data(),buffer->dataPointer(),buffer->byteLength());buffer->changeContents(cx,contents,OwnsData);}buffer->setIsPreparedForAsmJS();returntrue;}ArrayBufferObject::BufferContentsArrayBufferObject::createMappedContents(intfd,size_toffset,size_tlength){void*data=AllocateMappedContent(fd,offset,length,ARRAY_BUFFER_ALIGNMENT);MemProfiler::SampleNative(data,length);returnBufferContents::create<MAPPED>(data);}uint8_t*ArrayBufferObject::inlineDataPointer()const{returnstatic_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));}uint8_t*ArrayBufferObject::dataPointer()const{returnstatic_cast<uint8_t*>(getSlot(DATA_SLOT).toPrivate());}SharedMem<uint8_t*>ArrayBufferObject::dataPointerShared()const{returnSharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());}voidArrayBufferObject::releaseData(FreeOp*fop){MOZ_ASSERT(ownsData());switch(bufferKind()){casePLAIN:fop->free_(dataPointer());break;caseMAPPED:MemProfiler::RemoveNative(dataPointer());DeallocateMappedContent(dataPointer(),byteLength());break;caseWASM:WasmArrayRawBuffer::Release(dataPointer());break;caseKIND_MASK:MOZ_CRASH("bad bufferKind()");}}voidArrayBufferObject::setDataPointer(BufferContentscontents,OwnsStateownsData){setSlot(DATA_SLOT,PrivateValue(contents.data()));setOwnsData(ownsData);setFlags((flags()&~KIND_MASK)|contents.kind());}uint32_tArrayBufferObject::byteLength()const{returngetSlot(BYTE_LENGTH_SLOT).toInt32();}voidArrayBufferObject::setByteLength(uint32_tlength){MOZ_ASSERT(length<=INT32_MAX);setSlot(BYTE_LENGTH_SLOT,Int32Value(length));}size_tArrayBufferObject::wasmMappedSize()const{if(isWasm())returncontents().wasmBuffer()->mappedSize();returnbyteLength();}size_tjs::WasmArrayBufferMappedSize(constArrayBufferObjectMaybeShared*buf){if(buf->is<ArrayBufferObject>())returnbuf->as<ArrayBufferObject>().wasmMappedSize();#ifdef WASM_HUGE_MEMORYreturnwasm::HugeMappedSize;#elsereturnbuf->as<SharedArrayBufferObject>().byteLength();#endif}Maybe<uint32_t>ArrayBufferObject::wasmMaxSize()const{if(isWasm())returncontents().wasmBuffer()->maxSize();elsereturnSome<uint32_t>(byteLength());}Maybe<uint32_t>js::WasmArrayBufferMaxSize(constArrayBufferObjectMaybeShared*buf){if(buf->is<ArrayBufferObject>())returnbuf->as<ArrayBufferObject>().wasmMaxSize();returnSome(buf->as<SharedArrayBufferObject>().byteLength());}/* static */boolArrayBufferObject::wasmGrowToSizeInPlace(uint32_tnewSize,HandleArrayBufferObjectoldBuf,MutableHandleArrayBufferObjectnewBuf,JSContext*cx){// On failure, do not throw and ensure that the original buffer is// unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the// wasm-visible length of the buffer has been increased so it must be the// last fallible operation.// byteLength can be at most INT32_MAX. Note: if this hard limit changes,// update the clamping behavior in wasm::DecodeMemoryLimits and remove this// comment as well as the one in wasmMovingGrowToSize.if(newSize>INT32_MAX)returnfalse;newBuf.set(ArrayBufferObject::createEmpty(cx));if(!newBuf){cx->clearPendingException();returnfalse;}if(!oldBuf->contents().wasmBuffer()->growToSizeInPlace(oldBuf->byteLength(),newSize))returnfalse;boolhasStealableContents=true;BufferContentscontents=ArrayBufferObject::stealContents(cx,oldBuf,hasStealableContents);MOZ_ASSERT(contents);newBuf->initialize(newSize,contents,OwnsData);returntrue;}#ifndef WASM_HUGE_MEMORY/* static */boolArrayBufferObject::wasmMovingGrowToSize(uint32_tnewSize,HandleArrayBufferObjectoldBuf,MutableHandleArrayBufferObjectnewBuf,JSContext*cx){// On failure, do not throw and ensure that the original buffer is// unmodified and valid.// byteLength can be at most INT32_MAX.// See comment in wasmGrowToSizeInPlace about wasm::DecodeMemoryLimits.if(newSize>INT32_MAX)returnfalse;if(newSize<=oldBuf->wasmBoundsCheckLimit()||oldBuf->contents().wasmBuffer()->extendMappedSize(newSize)){returnwasmGrowToSizeInPlace(newSize,oldBuf,newBuf,cx);}newBuf.set(ArrayBufferObject::createEmpty(cx));if(!newBuf){cx->clearPendingException();returnfalse;}WasmArrayRawBuffer*newRawBuf=WasmArrayRawBuffer::Allocate(newSize,Nothing());if(!newRawBuf)returnfalse;BufferContentscontents=BufferContents::create<WASM>(newRawBuf->dataPointer());newBuf->initialize(newSize,contents,OwnsData);memcpy(newBuf->dataPointer(),oldBuf->dataPointer(),oldBuf->byteLength());ArrayBufferObject::detach(cx,oldBuf,BufferContents::createPlain(nullptr));returntrue;}uint32_tArrayBufferObject::wasmBoundsCheckLimit()const{if(isWasm())returncontents().wasmBuffer()->boundsCheckLimit();elsereturnbyteLength();}uint32_tArrayBufferObjectMaybeShared::wasmBoundsCheckLimit()const{if(is<ArrayBufferObject>())returnas<ArrayBufferObject>().wasmBoundsCheckLimit();returnas<SharedArrayBufferObject>().byteLength();}#endifuint32_tArrayBufferObject::flags()const{returnuint32_t(getSlot(FLAGS_SLOT).toInt32());}voidArrayBufferObject::setFlags(uint32_tflags){setSlot(FLAGS_SLOT,Int32Value(flags));}ArrayBufferObject*ArrayBufferObject::create(JSContext*cx,uint32_tnbytes,BufferContentscontents,OwnsStateownsState/* = OwnsData */,HandleObjectproto/* = nullptr */,NewObjectKindnewKind/* = GenericObject */){MOZ_ASSERT_IF(contents.kind()==MAPPED,contents);// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).// Refuse to allocate too large buffers, currently limited to ~2 GiB.if(nbytes>INT32_MAX){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnnullptr;}// If we need to allocate data, try to use a larger object size class so// that the array buffer's data can be allocated inline with the object.// The extra space will be left unused by the object's fixed slots and// available for the buffer's data, see NewObject().size_treservedSlots=JSCLASS_RESERVED_SLOTS(&class_);size_tnslots=reservedSlots;boolallocated=false;if(contents){if(ownsState==OwnsData){// The ABO is taking ownership, so account the bytes against the zone.size_tnAllocated=nbytes;if(contents.kind()==MAPPED)nAllocated=JS_ROUNDUP(nbytes,js::gc::SystemPageSize());elseif(contents.kind()==WASM)nAllocated=contents.wasmBuffer()->allocatedBytes();cx->zone()->updateMallocCounter(nAllocated);}}else{MOZ_ASSERT(ownsState==OwnsData);size_tusableSlots=NativeObject::MAX_FIXED_SLOTS-reservedSlots;if(nbytes<=usableSlots*sizeof(Value)){intnewSlots=JS_HOWMANY(nbytes,sizeof(Value));MOZ_ASSERT(int(nbytes)<=newSlots*int(sizeof(Value)));nslots=reservedSlots+newSlots;contents=BufferContents::createPlain(nullptr);}else{contents=AllocateArrayBufferContents(cx,nbytes);if(!contents)returnnullptr;allocated=true;}}MOZ_ASSERT(!(class_.flags&JSCLASS_HAS_PRIVATE));gc::AllocKindallocKind=GetGCObjectKind(nslots);AutoSetNewObjectMetadatametadata(cx);Rooted<ArrayBufferObject*>obj(cx,NewObjectWithClassProto<ArrayBufferObject>(cx,proto,allocKind,newKind));if(!obj){if(allocated)js_free(contents.data());returnnullptr;}MOZ_ASSERT(obj->getClass()==&class_);MOZ_ASSERT(!gc::IsInsideNursery(obj));if(!contents){void*data=obj->inlineDataPointer();memset(data,0,nbytes);obj->initialize(nbytes,BufferContents::createPlain(data),DoesntOwnData);}else{obj->initialize(nbytes,contents,ownsState);}returnobj;}ArrayBufferObject*ArrayBufferObject::create(JSContext*cx,uint32_tnbytes,HandleObjectproto/* = nullptr */,NewObjectKindnewKind/* = GenericObject */){returncreate(cx,nbytes,BufferContents::createPlain(nullptr),OwnsState::OwnsData,proto);}ArrayBufferObject*ArrayBufferObject::createEmpty(JSContext*cx){AutoSetNewObjectMetadatametadata(cx);ArrayBufferObject*obj=NewObjectWithClassProto<ArrayBufferObject>(cx,nullptr);if(!obj)returnnullptr;obj->initEmpty();returnobj;}/* static */ArrayBufferObject::BufferContentsArrayBufferObject::externalizeContents(JSContext*cx,Handle<ArrayBufferObject*>buffer,boolhasStealableContents){MOZ_ASSERT(buffer->isPlain(),"Only support doing this on plain ABOs");MOZ_ASSERT(!buffer->isDetached(),"must have contents to externalize");MOZ_ASSERT_IF(hasStealableContents,buffer->hasStealableContents());BufferContentscontents(buffer->dataPointer(),buffer->bufferKind());if(hasStealableContents){buffer->setOwnsData(DoesntOwnData);returncontents;}// Create a new chunk of memory to return since we cannot steal the// existing contents away from the buffer.BufferContentsnewContents=AllocateArrayBufferContents(cx,buffer->byteLength());if(!newContents)returnBufferContents::createPlain(nullptr);memcpy(newContents.data(),contents.data(),buffer->byteLength());buffer->changeContents(cx,newContents,DoesntOwnData);returnnewContents;}/* static */ArrayBufferObject::BufferContentsArrayBufferObject::stealContents(JSContext*cx,Handle<ArrayBufferObject*>buffer,boolhasStealableContents){// While wasm buffers cannot generally be transferred by content, the// stealContents() is used internally by the impl of memory growth.MOZ_ASSERT_IF(hasStealableContents,buffer->hasStealableContents()||(buffer->isWasm()&&!buffer->isPreparedForAsmJS()));assertSameCompartment(cx,buffer);BufferContentsoldContents(buffer->dataPointer(),buffer->bufferKind());if(hasStealableContents){// Return the old contents and reset the detached buffer's data// pointer. This pointer should never be accessed.autonewContents=BufferContents::createPlain(nullptr);buffer->setOwnsData(DoesntOwnData);// Do not free the stolen data.ArrayBufferObject::detach(cx,buffer,newContents);buffer->setOwnsData(DoesntOwnData);// Do not free the nullptr.returnoldContents;}// Create a new chunk of memory to return since we cannot steal the// existing contents away from the buffer.BufferContentscontentsCopy=AllocateArrayBufferContents(cx,buffer->byteLength());if(!contentsCopy)returnBufferContents::createPlain(nullptr);if(buffer->byteLength()>0)memcpy(contentsCopy.data(),oldContents.data(),buffer->byteLength());ArrayBufferObject::detach(cx,buffer,oldContents);returncontentsCopy;}/* static */voidArrayBufferObject::addSizeOfExcludingThis(JSObject*obj,mozilla::MallocSizeOfmallocSizeOf,JS::ClassInfo*info){ArrayBufferObject&buffer=AsArrayBuffer(obj);if(!buffer.ownsData())return;switch(buffer.bufferKind()){casePLAIN:if(buffer.isPreparedForAsmJS())info->objectsMallocHeapElementsAsmJS+=mallocSizeOf(buffer.dataPointer());elseinfo->objectsMallocHeapElementsNormal+=mallocSizeOf(buffer.dataPointer());break;caseMAPPED:info->objectsNonHeapElementsNormal+=buffer.byteLength();break;caseWASM:info->objectsNonHeapElementsWasm+=buffer.byteLength();MOZ_ASSERT(buffer.wasmMappedSize()>=buffer.byteLength());info->wasmGuardPages+=buffer.wasmMappedSize()-buffer.byteLength();break;caseKIND_MASK:MOZ_CRASH("bad bufferKind()");}}/* static */voidArrayBufferObject::finalize(FreeOp*fop,JSObject*obj){ArrayBufferObject&buffer=obj->as<ArrayBufferObject>();if(buffer.ownsData())buffer.releaseData(fop);}/* static */voidArrayBufferObject::copyData(Handle<ArrayBufferObject*>toBuffer,uint32_ttoIndex,Handle<ArrayBufferObject*>fromBuffer,uint32_tfromIndex,uint32_tcount){MOZ_ASSERT(toBuffer->byteLength()>=count);MOZ_ASSERT(toBuffer->byteLength()>=toIndex+count);MOZ_ASSERT(fromBuffer->byteLength()>=fromIndex);MOZ_ASSERT(fromBuffer->byteLength()>=fromIndex+count);memcpy(toBuffer->dataPointer()+toIndex,fromBuffer->dataPointer()+fromIndex,count);}/* static */voidArrayBufferObject::trace(JSTracer*trc,JSObject*obj){// If this buffer is associated with an inline typed object,// fix up the data pointer if the typed object was moved.ArrayBufferObject&buf=obj->as<ArrayBufferObject>();if(!buf.forInlineTypedObject())return;JSObject*view=MaybeForwarded(buf.firstView());MOZ_ASSERT(view&&view->is<InlineTransparentTypedObject>());TraceManuallyBarrieredEdge(trc,&view,"array buffer inline typed object owner");buf.setSlot(DATA_SLOT,PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));}/* static */voidArrayBufferObject::objectMoved(JSObject*obj,constJSObject*old){ArrayBufferObject&dst=obj->as<ArrayBufferObject>();constArrayBufferObject&src=old->as<ArrayBufferObject>();// Fix up possible inline data pointer.if(src.hasInlineData())dst.setSlot(DATA_SLOT,PrivateValue(dst.inlineDataPointer()));}ArrayBufferViewObject*ArrayBufferObject::firstView(){returngetSlot(FIRST_VIEW_SLOT).isObject()?static_cast<ArrayBufferViewObject*>(&getSlot(FIRST_VIEW_SLOT).toObject()):nullptr;}voidArrayBufferObject::setFirstView(ArrayBufferViewObject*view){setSlot(FIRST_VIEW_SLOT,ObjectOrNullValue(view));}boolArrayBufferObject::addView(JSContext*cx,JSObject*viewArg){// Note: we don't pass in an ArrayBufferViewObject as the argument due to// tricky inheritance in the various view classes. View classes do not// inherit from ArrayBufferViewObject so won't be upcast automatically.MOZ_ASSERT(viewArg->is<ArrayBufferViewObject>()||viewArg->is<TypedObject>());ArrayBufferViewObject*view=static_cast<ArrayBufferViewObject*>(viewArg);if(!firstView()){setFirstView(view);returntrue;}returncx->compartment()->innerViews.get().addView(cx,this,view);}/* * InnerViewTable */staticsize_tVIEW_LIST_MAX_LENGTH=500;boolInnerViewTable::addView(JSContext*cx,ArrayBufferObject*buffer,ArrayBufferViewObject*view){// ArrayBufferObject entries are only added when there are multiple views.MOZ_ASSERT(buffer->firstView());if(!map.initialized()&&!map.init()){ReportOutOfMemory(cx);returnfalse;}Map::AddPtrp=map.lookupForAdd(buffer);MOZ_ASSERT(!gc::IsInsideNursery(buffer));booladdToNursery=nurseryKeysValid&&gc::IsInsideNursery(view);if(p){ViewVector&views=p->value();MOZ_ASSERT(!views.empty());if(addToNursery){// Only add the entry to |nurseryKeys| if it isn't already there.if(views.length()>=VIEW_LIST_MAX_LENGTH){// To avoid quadratic blowup, skip the loop below if we end up// adding enormous numbers of views for the same object.nurseryKeysValid=false;}else{for(size_ti=0;i<views.length();i++){if(gc::IsInsideNursery(views[i])){addToNursery=false;break;}}}}if(!views.append(view)){ReportOutOfMemory(cx);returnfalse;}}else{if(!map.add(p,buffer,ViewVector())){ReportOutOfMemory(cx);returnfalse;}// ViewVector has one inline element, so the first insertion is// guaranteed to succeed.MOZ_ALWAYS_TRUE(p->value().append(view));}if(addToNursery&&!nurseryKeys.append(buffer))nurseryKeysValid=false;returntrue;}InnerViewTable::ViewVector*InnerViewTable::maybeViewsUnbarriered(ArrayBufferObject*buffer){if(!map.initialized())returnnullptr;Map::Ptrp=map.lookup(buffer);if(p)return&p->value();returnnullptr;}voidInnerViewTable::removeViews(ArrayBufferObject*buffer){Map::Ptrp=map.lookup(buffer);MOZ_ASSERT(p);map.remove(p);}/* static */boolInnerViewTable::sweepEntry(JSObject**pkey,ViewVector&views){if(IsAboutToBeFinalizedUnbarriered(pkey))returntrue;MOZ_ASSERT(!views.empty());for(size_ti=0;i<views.length();i++){if(IsAboutToBeFinalizedUnbarriered(&views[i])){views[i--]=views.back();views.popBack();}}returnviews.empty();}voidInnerViewTable::sweep(){MOZ_ASSERT(nurseryKeys.empty());map.sweep();}voidInnerViewTable::sweepAfterMinorGC(){MOZ_ASSERT(needsSweepAfterMinorGC());if(nurseryKeysValid){for(size_ti=0;i<nurseryKeys.length();i++){JSObject*buffer=MaybeForwarded(nurseryKeys[i]);Map::Ptrp=map.lookup(buffer);if(!p)continue;if(sweepEntry(&p->mutableKey(),p->value()))map.remove(buffer);}nurseryKeys.clear();}else{// Do the required sweeping by looking at every map entry.nurseryKeys.clear();sweep();nurseryKeysValid=true;}}size_tInnerViewTable::sizeOfExcludingThis(mozilla::MallocSizeOfmallocSizeOf){if(!map.initialized())return0;size_tvectorSize=0;for(Map::Enume(map);!e.empty();e.popFront())vectorSize+=e.front().value().sizeOfExcludingThis(mallocSizeOf);returnvectorSize+map.sizeOfExcludingThis(mallocSizeOf)+nurseryKeys.sizeOfExcludingThis(mallocSizeOf);}/* * ArrayBufferViewObject *//* * This method is used to trace TypedArrayObjects and DataViewObjects. We need * a custom tracer to move the object's data pointer if its owner was moved and * stores its data inline. *//* static */voidArrayBufferViewObject::trace(JSTracer*trc,JSObject*objArg){NativeObject*obj=&objArg->as<NativeObject>();HeapSlot&bufSlot=obj->getFixedSlotRef(TypedArrayObject::BUFFER_SLOT);TraceEdge(trc,&bufSlot,"typedarray.buffer");// Update obj's data pointer if it moved.if(bufSlot.isObject()){if(IsArrayBuffer(&bufSlot.toObject())){ArrayBufferObject&buf=AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));uint32_toffset=uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());MOZ_ASSERT(offset<=INT32_MAX);if(buf.forInlineTypedObject()){MOZ_ASSERT(buf.dataPointer()!=nullptr);// The data is inline with an InlineTypedObject associated with the// buffer. Get a new address for the typed object if it moved.JSObject*view=buf.firstView();// Mark the object to move it into the tenured space.TraceManuallyBarrieredEdge(trc,&view,"typed array nursery owner");MOZ_ASSERT(view->is<InlineTypedObject>());MOZ_ASSERT(view!=obj);void*srcData=obj->getPrivate();void*dstData=view->as<InlineTypedObject>().inlineTypedMemForGC()+offset;obj->setPrivateUnbarriered(dstData);// We can't use a direct forwarding pointer here, as there might// not be enough bytes available, and other views might have data// pointers whose forwarding pointers would overlap this one.if(trc->isTenuringTracer()){Nursery&nursery=obj->zoneFromAnyThread()->group()->nursery();nursery.maybeSetForwardingPointer(trc,srcData,dstData,/* direct = */false);}}else{MOZ_ASSERT_IF(buf.dataPointer()==nullptr,offset==0);// The data may or may not be inline with the buffer. The buffer// can only move during a compacting GC, in which case its// objectMoved hook has already updated the buffer's data pointer.obj->initPrivate(buf.dataPointer()+offset);}}}}template<>boolJSObject::is<js::ArrayBufferViewObject>()const{returnis<DataViewObject>()||is<TypedArrayObject>();}template<>boolJSObject::is<js::ArrayBufferObjectMaybeShared>()const{returnis<ArrayBufferObject>()||is<SharedArrayBufferObject>();}voidArrayBufferViewObject::notifyBufferDetached(JSContext*cx,void*newData){if(is<DataViewObject>()){if(as<DataViewObject>().isSharedMemory())return;as<DataViewObject>().notifyBufferDetached(newData);}elseif(is<TypedArrayObject>()){if(as<TypedArrayObject>().isSharedMemory())return;as<TypedArrayObject>().notifyBufferDetached(cx,newData);}else{as<OutlineTypedObject>().notifyBufferDetached(newData);}}uint8_t*ArrayBufferViewObject::dataPointerUnshared(constJS::AutoRequireNoGC&nogc){if(is<DataViewObject>()){MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());returnstatic_cast<uint8_t*>(as<DataViewObject>().dataPointerUnshared());}if(is<TypedArrayObject>()){MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());returnstatic_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());}returnas<TypedObject>().typedMem(nogc);}#ifdef DEBUGboolArrayBufferViewObject::isSharedMemory(){if(is<TypedArrayObject>())returnas<TypedArrayObject>().isSharedMemory();returnfalse;}#endifvoidArrayBufferViewObject::setDataPointerUnshared(uint8_t*data){if(is<DataViewObject>()){MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());as<DataViewObject>().setPrivate(data);}elseif(is<TypedArrayObject>()){MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());as<TypedArrayObject>().setPrivate(data);}elseif(is<OutlineTypedObject>()){as<OutlineTypedObject>().setData(data);}else{MOZ_CRASH();}}/* static */ArrayBufferObjectMaybeShared*ArrayBufferViewObject::bufferObject(JSContext*cx,Handle<ArrayBufferViewObject*>thisObject){if(thisObject->is<TypedArrayObject>()){Rooted<TypedArrayObject*>typedArray(cx,&thisObject->as<TypedArrayObject>());if(!TypedArrayObject::ensureHasBuffer(cx,typedArray))returnnullptr;returnthisObject->as<TypedArrayObject>().bufferEither();}MOZ_ASSERT(thisObject->is<DataViewObject>());return&thisObject->as<DataViewObject>().arrayBufferEither();}/* JS Friend API */JS_FRIEND_API(bool)JS_IsArrayBufferViewObject(JSObject*obj){obj=CheckedUnwrap(obj);returnobj&&obj->is<ArrayBufferViewObject>();}JS_FRIEND_API(JSObject*)js::UnwrapArrayBufferView(JSObject*obj){if(JSObject*unwrapped=CheckedUnwrap(obj))returnunwrapped->is<ArrayBufferViewObject>()?unwrapped:nullptr;returnnullptr;}JS_FRIEND_API(uint32_t)JS_GetArrayBufferByteLength(JSObject*obj){obj=CheckedUnwrap(obj);returnobj?AsArrayBuffer(obj).byteLength():0;}JS_FRIEND_API(uint8_t*)JS_GetArrayBufferData(JSObject*obj,bool*isSharedMemory,constJS::AutoCheckCannotGC&){obj=CheckedUnwrap(obj);if(!obj)returnnullptr;if(!IsArrayBuffer(obj))returnnullptr;*isSharedMemory=false;returnAsArrayBuffer(obj).dataPointer();}JS_FRIEND_API(bool)JS_DetachArrayBuffer(JSContext*cx,HandleObjectobj){AssertHeapIsIdle();CHECK_REQUEST(cx);assertSameCompartment(cx,obj);if(!obj->is<ArrayBufferObject>()){JS_ReportErrorASCII(cx,"ArrayBuffer object required");returnfalse;}Rooted<ArrayBufferObject*>buffer(cx,&obj->as<ArrayBufferObject>());if(buffer->isWasm()||buffer->isPreparedForAsmJS()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_WASM_NO_TRANSFER);returnfalse;}ArrayBufferObject::BufferContentsnewContents=buffer->hasStealableContents()?ArrayBufferObject::BufferContents::createPlain(nullptr):buffer->contents();ArrayBufferObject::detach(cx,buffer,newContents);returntrue;}JS_FRIEND_API(bool)JS_IsDetachedArrayBufferObject(JSObject*obj){obj=CheckedUnwrap(obj);if(!obj)returnfalse;returnobj->is<ArrayBufferObject>()&&obj->as<ArrayBufferObject>().isDetached();}JS_FRIEND_API(JSObject*)JS_NewArrayBuffer(JSContext*cx,uint32_tnbytes){AssertHeapIsIdle();CHECK_REQUEST(cx);MOZ_ASSERT(nbytes<=INT32_MAX);returnArrayBufferObject::create(cx,nbytes);}JS_PUBLIC_API(JSObject*)JS_NewArrayBufferWithContents(JSContext*cx,size_tnbytes,void*data){AssertHeapIsIdle();CHECK_REQUEST(cx);MOZ_ASSERT_IF(!data,nbytes==0);ArrayBufferObject::BufferContentscontents=ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);returnArrayBufferObject::create(cx,nbytes,contents,ArrayBufferObject::OwnsData,/* proto = */nullptr,TenuredObject);}JS_PUBLIC_API(JSObject*)JS_NewArrayBufferWithExternalContents(JSContext*cx,size_tnbytes,void*data){AssertHeapIsIdle();CHECK_REQUEST(cx);MOZ_ASSERT_IF(!data,nbytes==0);ArrayBufferObject::BufferContentscontents=ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);returnArrayBufferObject::create(cx,nbytes,contents,ArrayBufferObject::DoesntOwnData,/* proto = */nullptr,TenuredObject);}JS_FRIEND_API(bool)JS_IsArrayBufferObject(JSObject*obj){obj=CheckedUnwrap(obj);returnobj&&obj->is<ArrayBufferObject>();}JS_FRIEND_API(bool)JS_ArrayBufferHasData(JSObject*obj){returnCheckedUnwrap(obj)->as<ArrayBufferObject>().hasData();}JS_FRIEND_API(JSObject*)js::UnwrapArrayBuffer(JSObject*obj){if(JSObject*unwrapped=CheckedUnwrap(obj))returnunwrapped->is<ArrayBufferObject>()?unwrapped:nullptr;returnnullptr;}JS_FRIEND_API(JSObject*)js::UnwrapSharedArrayBuffer(JSObject*obj){if(JSObject*unwrapped=CheckedUnwrap(obj))returnunwrapped->is<SharedArrayBufferObject>()?unwrapped:nullptr;returnnullptr;}JS_PUBLIC_API(void*)JS_ExternalizeArrayBufferContents(JSContext*cx,HandleObjectobj){AssertHeapIsIdle();CHECK_REQUEST(cx);assertSameCompartment(cx,obj);if(!obj->is<ArrayBufferObject>()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_TYPED_ARRAY_BAD_ARGS);returnnullptr;}Handle<ArrayBufferObject*>buffer=obj.as<ArrayBufferObject>();if(!buffer->isPlain()){// This operation isn't supported on mapped or wsm ArrayBufferObjects.JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_TYPED_ARRAY_BAD_ARGS);returnnullptr;}if(buffer->isDetached()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_TYPED_ARRAY_DETACHED);returnnullptr;}// The caller assumes that a plain malloc'd buffer is returned.// hasStealableContents is true for mapped buffers, so we must additionally// require that the buffer is plain. In the future, we could consider// returning something that handles releasing the memory.boolhasStealableContents=buffer->hasStealableContents();returnArrayBufferObject::externalizeContents(cx,buffer,hasStealableContents).data();}JS_PUBLIC_API(void*)JS_StealArrayBufferContents(JSContext*cx,HandleObjectobjArg){AssertHeapIsIdle();CHECK_REQUEST(cx);assertSameCompartment(cx,objArg);JSObject*obj=CheckedUnwrap(objArg);if(!obj)returnnullptr;if(!obj->is<ArrayBufferObject>()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_TYPED_ARRAY_BAD_ARGS);returnnullptr;}Rooted<ArrayBufferObject*>buffer(cx,&obj->as<ArrayBufferObject>());if(buffer->isDetached()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_TYPED_ARRAY_DETACHED);returnnullptr;}if(buffer->isWasm()||buffer->isPreparedForAsmJS()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_WASM_NO_TRANSFER);returnnullptr;}// The caller assumes that a plain malloc'd buffer is returned.// hasStealableContents is true for mapped buffers, so we must additionally// require that the buffer is plain. In the future, we could consider// returning something that handles releasing the memory.boolhasStealableContents=buffer->hasStealableContents()&&buffer->isPlain();AutoCompartmentac(cx,buffer);returnArrayBufferObject::stealContents(cx,buffer,hasStealableContents).data();}JS_PUBLIC_API(JSObject*)JS_NewMappedArrayBufferWithContents(JSContext*cx,size_tnbytes,void*data){AssertHeapIsIdle();CHECK_REQUEST(cx);MOZ_ASSERT(data);ArrayBufferObject::BufferContentscontents=ArrayBufferObject::BufferContents::create<ArrayBufferObject::MAPPED>(data);returnArrayBufferObject::create(cx,nbytes,contents,ArrayBufferObject::OwnsData,/* proto = */nullptr,TenuredObject);}JS_PUBLIC_API(void*)JS_CreateMappedArrayBufferContents(intfd,size_toffset,size_tlength){returnArrayBufferObject::createMappedContents(fd,offset,length).data();}JS_PUBLIC_API(void)JS_ReleaseMappedArrayBufferContents(void*contents,size_tlength){MemProfiler::RemoveNative(contents);DeallocateMappedContent(contents,length);}JS_FRIEND_API(bool)JS_IsMappedArrayBufferObject(JSObject*obj){obj=CheckedUnwrap(obj);if(!obj)returnfalse;returnobj->is<ArrayBufferObject>()&&obj->as<ArrayBufferObject>().isMapped();}JS_FRIEND_API(void*)JS_GetArrayBufferViewData(JSObject*obj,bool*isSharedMemory,constJS::AutoCheckCannotGC&){obj=CheckedUnwrap(obj);if(!obj)returnnullptr;if(obj->is<DataViewObject>()){DataViewObject&dv=obj->as<DataViewObject>();*isSharedMemory=dv.isSharedMemory();returndv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory flag*/);}TypedArrayObject&ta=obj->as<TypedArrayObject>();*isSharedMemory=ta.isSharedMemory();returnta.viewDataEither().unwrap(/*safe - caller sees isSharedMemory flag*/);}JS_FRIEND_API(JSObject*)JS_GetArrayBufferViewBuffer(JSContext*cx,HandleObjectobjArg,bool*isSharedMemory){AssertHeapIsIdle();CHECK_REQUEST(cx);assertSameCompartment(cx,objArg);JSObject*obj=CheckedUnwrap(objArg);if(!obj)returnnullptr;MOZ_ASSERT(obj->is<ArrayBufferViewObject>());Rooted<ArrayBufferViewObject*>viewObject(cx,static_cast<ArrayBufferViewObject*>(obj));ArrayBufferObjectMaybeShared*buffer=ArrayBufferViewObject::bufferObject(cx,viewObject);*isSharedMemory=buffer->is<SharedArrayBufferObject>();returnbuffer;}JS_FRIEND_API(uint32_t)JS_GetArrayBufferViewByteLength(JSObject*obj){obj=CheckedUnwrap(obj);if(!obj)return0;returnobj->is<DataViewObject>()?obj->as<DataViewObject>().byteLength():obj->as<TypedArrayObject>().byteLength();}JS_FRIEND_API(JSObject*)JS_GetObjectAsArrayBufferView(JSObject*obj,uint32_t*length,bool*isSharedMemory,uint8_t**data){if(!(obj=CheckedUnwrap(obj)))returnnullptr;if(!(obj->is<ArrayBufferViewObject>()))returnnullptr;js::GetArrayBufferViewLengthAndData(obj,length,isSharedMemory,data);returnobj;}JS_FRIEND_API(void)js::GetArrayBufferViewLengthAndData(JSObject*obj,uint32_t*length,bool*isSharedMemory,uint8_t**data){MOZ_ASSERT(obj->is<ArrayBufferViewObject>());*length=obj->is<DataViewObject>()?obj->as<DataViewObject>().byteLength():obj->as<TypedArrayObject>().byteLength();if(obj->is<DataViewObject>()){DataViewObject&dv=obj->as<DataViewObject>();*isSharedMemory=dv.isSharedMemory();*data=static_cast<uint8_t*>(dv.dataPointerEither().unwrap(/*safe - caller sees isShared flag*/));}else{TypedArrayObject&ta=obj->as<TypedArrayObject>();*isSharedMemory=ta.isSharedMemory();*data=static_cast<uint8_t*>(ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/));}}JS_FRIEND_API(JSObject*)JS_GetObjectAsArrayBuffer(JSObject*obj,uint32_t*length,uint8_t**data){if(!(obj=CheckedUnwrap(obj)))returnnullptr;if(!IsArrayBuffer(obj))returnnullptr;*length=AsArrayBuffer(obj).byteLength();*data=AsArrayBuffer(obj).dataPointer();returnobj;}JS_FRIEND_API(void)js::GetArrayBufferLengthAndData(JSObject*obj,uint32_t*length,bool*isSharedMemory,uint8_t**data){MOZ_ASSERT(IsArrayBuffer(obj));*length=AsArrayBuffer(obj).byteLength();*data=AsArrayBuffer(obj).dataPointer();*isSharedMemory=false;}